package com.lagou.edu.bean;

import com.lagou.edu.anno.Autowired;
import com.lagou.edu.anno.Component;
import com.lagou.edu.anno.Transaction;
import com.lagou.edu.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理工厂
 * @author wsz
 * @date 2021/10/18 19:51
 **/
@Component
public class ProxyFactory {

    @Autowired
    private TransactionManager transactionManager;

    /**
     * 获取代理类
     * @param target
     * @return
     */
    public Object getProxy(Object target) {
        // TODO 1.判断类上是否存在@Transaction，类全局事务
        Class<?> targetClass = target.getClass();
        Transaction transaction = targetClass.getAnnotation(Transaction.class);

        Class<?>[] interfaces = targetClass.getInterfaces();
        if (interfaces == null || interfaces.length == 0) {
            // TODO 2.非接口实现类：cglib代理
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(targetClass);
            enhancer.setCallback(new TransactionSynchronizer(transaction != null, target));
            return enhancer.create();
        }

        // TODO 3.接口实现类：jdk动态代理
        return Proxy.newProxyInstance(targetClass.getClassLoader(), interfaces, new TransactionSynchronizer(transaction != null, target));
    }

    /**
     * 事务同步
     */
    private class TransactionSynchronizer implements InvocationHandler, MethodInterceptor {
        // 是否类事务
        private boolean transactionFlag;
        // 被代理对象
        private Object targetObject;

        public TransactionSynchronizer(boolean transactionFlag, Object targetObject) {
            this.transactionFlag = transactionFlag;
            this.targetObject = targetObject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return doInvoke(method, args);
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            return doInvoke(method, args);
        }

        /**
         * 真正执行的invoke
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        public Object doInvoke(Method method, Object[] args) throws Throwable {
            // 1.判断方法是否存在@Transaction
            Method[] declaredMethods = targetObject.getClass().getDeclaredMethods();
            Method source = null;
            for (Method m : declaredMethods) {
                if (m.getName().equals(method.getName())) {
                    source = m;
                }
            }
            Transaction methodTransaction = source == null ? null : source.getAnnotation(Transaction.class);
            // 2.存在事务
            if (transactionFlag || methodTransaction != null) {
                Object result;
                try {
                    transactionManager.beginTransaction();
                    result = method.invoke(targetObject, args);
                    transactionManager.commit();
                } catch (Exception ex) {
                    ex.printStackTrace();
                    transactionManager.rollback();
                    throw ex.getCause();
                }
                return result;
            }
            // 3.不存在事务
            return method.invoke(targetObject, args);
        }
    }

}
